//指定状态
const PENDING = 'PENDING';
const FULLFILLED = 'FULLFILLED';
const REJECTED = 'REJECTED';

class MyPromise {
  constructor(executor) {
    //初始化状态
    this.status = PENDING;
    //PENDING状态下，value和reason是不确定的，只有转了成功态或失败态才能确定
    this.value = undefined;
    this.reason = undefined;
    //初始化收集then()回调的容器
    this.onFullfilledCBs = [];
    this.onRejectedCBs = [];
    //初始化resolve和reject函数
    const resolve = (value) => {
      if (value instanceof MyPromise) {
        value.then(resolve, reject);
        return;
      }

      //状态转变之前必须是PENDING状态，并且转变之后就固化状态
      if (this.status === PENDING) {
        this.status = FULLFILLED;
        this.value = value;
        //发布执行回调队列里的回调
        this.onFullfilledCBs.forEach((fn) => fn());
      }
    };
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCBs.forEach((fn) => fn());
      }
    };

    try {
      //假如在executor()执行后尝试捕获 在函数内部定义throw new Error()的错误
      executor(resolve, reject);
    } catch (e) {
      //将捕获的异常交给reject()方法去进行捕获
      reject(e);
    }
  }

  static resolve(value) {
    return new MyPromise((resolve, reject) => {
      resolve(value);
    });
  }

  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason);
    });
  }

  static all(promiseArr) {
    let resArr = [],
      idx = 0;

    return new MyPromise((resolve, reject) => {
      promiseArr.map((promise, index) => {
        if (isPromise(promise)) {
          promise.then((res) => {
            formatResArr(res, index, resolve);
          }, reject);
        } else {
          formatResArr(promise, index, resolve);
        }
      });
    });

    function formatResArr(value, index, resolve) {
      resArr[index] = value;
      //++idx 从1开始的索引，确保promise在异步情况时不一定拿到结果时 防止数组元素为空
      if (++idx === promiseArr.length) {
        resolve(resArr);
      }
    }
  }

  static allSettled(promiseArr) {
    let resArr = [],
      idx = 0;
    if (!isIterable(promiseArr)) {
      throw new TypeError(promiseArr + ' is not iterable');
    }
    return new MyPromise((resolve, reject) => {
      if (promiseArr.length === 0) resolve([]);
      promiseArr.map((promise, index) => {
        if (isPromise(promise)) {
          promise.then(
            (value) => {
              formatResArr('fulfilled', value, index, resolve);
            },
            (reason) => {
              formatResArr('rejected', reason, index, resolve);
            }
          );
        } else {
          formatResArr('fulfilled', promise, index, value);
        }
      });
    });

    function formatResArr(status, value, index, resolve) {
      switch (status) {
        case 'fulfilled':
          resArr[index] = {
            status,
            value
          };
          break;
        case 'rejected':
          resArr[index] = {
            status,
            reason: value
          };
          break;
        default:
          break;
      }
      if (++idx === promiseArr.length) {
        resolve(resArr);
      }
    }
  }

  static race(promiseArr) {
    return new MyPromise((resolve, reject) => {
      promiseArr.map((promise) => {
        if (isPromise(promise)) {
          promise.then(resolve, reject);
        } else {
          resolve(promise);
        }
      });
    });
  }

  /**
   * 定义可链式调用的then方法
   * https://promisesaplus.com/#the-then-method
   * @param {*} onFullfilled {可选}
   * @param {*} onRejected {可选}
   * @returns 新的promise对象
   */
  then(onFullfilled, onRejected) {
    /**
     * 情况 2.2.1 ： Both onFulfilled and onRejected are optional arguments:
     * onFullfilled, onRejected参数方法都是可选的
     * 没有传入onFullfilled或onRejected函数时对其做一个默认的函数
     */
    onFullfilled =
      typeof onFullfilled === 'function' ? onFullfilled : (value) => value;
    onRejected =
      typeof onRejected === 'function'
        ? onRejected
        : (reason) => {
            throw reason;
          };

    let promise2 = new MyPromise((resolve, reject) => {
      //当成功态时才能执行onFullfilled()方法
      if (this.status === FULLFILLED) {
        /**
         * PromiseA+规范中，声明了onFullfilled()或
         * onRejected()必须是一个异步的函数，避免阻塞后续程序。
         *
         * 定义一个setTimeout()宏任务方法可以实现函数异步化，
         * 还能让setTimeout()的回调函数往后再执行，此时可以拿到
         * 外部的promise2对象。
         */
        setTimeout(() => {
          //尝试捕获使用者抛出的异常错误
          try {
            /**
             * onFullfilled()或onRejected()具有返回值，
             * 有可能是普通值，也有可能是promise对象，
             * 普通值或promise对象是又使用者去定义的。
             */
            let x = onFullfilled(this.value);
            //对返回值进行处理
            //对新的promise进行处理 并传入内部的resolve和reject方法
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            // resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }
      /**
       * 当用户在executor()函数内部定义异步方法时，
       * 异步方法内部的resolve()或rejected()方法是无法正常使用的。
       * 原因是当前状态为PENDING，导致后续的then()里的回调函数无法正常执行。
       */
      //解决方法是对PENGDING状态对所有回调进行收集
      if (this.status === PENDING) {
        //利用发布订阅模式收集多个then()里的回调函数，这些函数由于是依次执行的顺序，所以需要收集起来。
        //用函数的写法可以让onFullfilled()执行时传入参数
        this.onFullfilledCBs.push(() => {
          setTimeout(() => {
            try {
              let x = onFullfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });

        this.onRejectedCBs.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });

    //因为每次then()都需要返回一个新的promise对象
    return promise2;
  }

  catch(errorCallback) {
    return this.then(null, errorCallback);
  }

  finally(finallyCallback) {
    return this.then(
      (value) => {
        return MyPromise.resolve(finallyCallback()).then(() => value);
      },
      (reason) => {
        return MyPromise.resolve(finallyCallback()).then(() => {
          throw reason;
        });
      }
    );
  }
}

/**
 * 专门处理内部promise和onFullfilled或onRejected方法返回值的函数，
 * 对应PromiseA+规范中 promise处理流程
 * https://promisesaplus.com/#the-promise-resolution-procedure
 * @param {*} promise2 一个then()返回的promise值
 * @param {*} x onFullfilled或onRejected方法返回值, 是then(()=>{ return }) 返回的值，有可能是普通值，有可能是promise
 * @param {*} resolve 内部的resolve方法
 * @param {*} reject  内部的reject方法
 */
function resolvePromise(promise2, x, resolve, reject) {
  /**
   * 处理情况 2.3.1 ： If promise and x refer to the same object,
   * reject promise with a TypeError as the reason.
   */
  if (x === promise2) {
    return reject(new TypeError('Chaining cycle detected for pormise'));
  }

  /**
   * 处理情况 2.3.3.3.3 : If both resolvePromise and rejectPromise are called,
   * or multiple calls to the same argument are made,
   * the first call takes precedence,
   * and any further calls are ignored.
   * 避免then((resolve, reject) => { resolve() reject() })同时执行resolve()和reject()
   */
  let called = false;

  /**
   * 处理情况 2.3.3 : Otherwise, if x is an object or function,
   */
  if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
    //这里try catch 是 避免x.then访问时被劫持(Object.defineProperty)并抛出异常的情况
    try {
      //onFulfilled()返回的值有可能是普通对象，也有可能是有promise对象，也有可能是一个函数
      let then = x.then;
      //假如该对象里有then()方法说明它是一个promise对象
      if (typeof then === 'function') {
        /**
         * 处理情况 2.3.3.3 : If then is a function, call it with x as this,
         * first argument resolvePromise,
         * and second argument rejectPromise,
         */
        //执行then()方法并更改指向为当前promise对象
        then.call(
          x,
          (y) => {
            if (called) return;
            //递归操作 对多层resolve(new Promise())的写法的实现
            resolvePromise(promise2, y, reject, reject);
            called = true;
          }, //onFulfilled()
          (r) => {
            if (called) return;
            reject(r);
            called = true;
          } //onRejected()
        );
      } else {
        //普通对象时直接resolve()抛出
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      reject(e);
      called = true;
    }
  } else {
    //普通值的情况时直接resolve()抛出
    resolve(x);
  }
}

function isPromise(x) {
  if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
    let then = x.then;
    return typeof then === 'function' ? true : false;
  } else {
    return false;
  }
}

function isIterable(value) {
  return (
    value !== null &&
    value !== undefined &&
    typeof value[Symbol.iterator] === 'function'
  );
}

module.exports = MyPromise;
